﻿
using System.Collections.Generic;

namespace SuperGo.SilverlightLib.History {
    public class GameHistory {
        private readonly List<HistoryEvent> _history = new List<HistoryEvent>();

        public HistoryEvent GetHistoryEvent(int turn) {
            if (turn < 0)
                throw new System.InvalidOperationException("Turn can not be less than zero.");
            if (turn >= _history.Count)
                throw new System.InvalidOperationException("Turn can not be greater than the total number of turns.");
            return _history[turn];
        }

        public GameHistory(int size, Player playerToStart) {
            _history.Add(new StartGameHistoryEvent(size, playerToStart));
        }

        public Board CurrentBoard {
            get { return CurrentHistoryEvent.Board; }
        }

        public HistoryEvent CurrentHistoryEvent {
            get { return _history[_history.Count - 1]; }
        }

        public int CurrentBlackCaptures {
            get { return CurrentHistoryEvent.BlackCaptures; }
        }

        public int CurrentWhiteCaptures {
            get { return CurrentHistoryEvent.WhiteCaptures; }
        }

        public Player CurrentPlayer {
            get { return CurrentGameState.ToPlayer(); }
        }

        public int Turn {
            get { return _history.Count; }
        }

        public bool LastMovePass {
            get { return CurrentHistoryEvent is PassHistoryEvent; }
        }

        public static GameState GetGameState(HistoryEvent currentHistoryEvent) {
            if (currentHistoryEvent is ResignHistoryEvent) {
                ResignHistoryEvent resignHistoryEvent = (ResignHistoryEvent)currentHistoryEvent;
                if (resignHistoryEvent.Player == Player.Black)
                    return GameState.BlackResigned;
                return GameState.WhiteResigned;
            }

            if (currentHistoryEvent is NegotiatingDeadStonesHistoryEvent) {
                return GameState.NegotiatingDeadStones;
            }

            if (currentHistoryEvent is GameOverHistoryEvent) {
                return GameState.DeadStonesMergedGameOver;
            }

            if (currentHistoryEvent is MoveHistoryEvent) {
                if (((MoveHistoryEvent)currentHistoryEvent).Player == Player.Black) {
                    return GameState.WhiteToMove;
                }
                return GameState.BlackToMove;
            }

            if (currentHistoryEvent is PassHistoryEvent) {
                if (((PassHistoryEvent)currentHistoryEvent).Player == Player.Black) {
                    return GameState.WhiteToMove;
                }
                return GameState.BlackToMove;
            }

            if (currentHistoryEvent is StartGameHistoryEvent) {
                if (((StartGameHistoryEvent)currentHistoryEvent).PlayerToStart == Player.White) {
                    return GameState.WhiteToMove;
                }
                return GameState.BlackToMove;
            }

            throw new GoException("Unknown game state. Current history event " + currentHistoryEvent);
        }

        public GameState CurrentGameState {
            get {
                return GetGameState(CurrentHistoryEvent);
            }
        }

        public void AddNegotiateDeadStones() {
            _history.Add(new NegotiatingDeadStonesHistoryEvent(CurrentBoard.Clone(), CurrentBlackCaptures, CurrentWhiteCaptures));
        }

        public void AddPass(Player player, bool storeInHistory) {
            PassHistoryEvent passHistoryEvent = new PassHistoryEvent(CurrentBoard.Clone(), player, CurrentBlackCaptures, CurrentWhiteCaptures);
            AddToHistory(storeInHistory, passHistoryEvent);
        }

        private void AddToHistory(bool storeInHistory, HistoryEvent historyEvent) {
            if (storeInHistory) {
                _history.Add(historyEvent);
            } else {
                _history[_history.Count - 1] = historyEvent;
            }
        }

        public void AddPass(bool storeInHistory) {
            AddPass(CurrentPlayer, storeInHistory);
        }

        public void AddMove(Board board, int x, int y, Player player, int captures, bool storeInHistory) {
            int whiteCaptures = CurrentWhiteCaptures;
            int blackCaptures = CurrentBlackCaptures;
            if (player == Player.White) {
                whiteCaptures += captures;
            } else {
                blackCaptures += captures;
            }

            MoveHistoryEvent latestMoveHistoryEvent = new MoveHistoryEvent(board, x, y, player, blackCaptures, whiteCaptures);
            AddToHistory(storeInHistory, latestMoveHistoryEvent);
        }

        public void AddResign() {
            _history.Add(new ResignHistoryEvent(CurrentBoard.Clone(), CurrentPlayer, CurrentBlackCaptures, CurrentWhiteCaptures));
        }

        /// <summary>
        /// This version of game over may add captures if dead stones were marked and then merged.
        /// </summary>
        public void AddGameOver(int blackCaptures, int whiteCaptures) {
            int newBlackCaptures = CurrentBlackCaptures + blackCaptures;
            int newWhiteCaptures = CurrentWhiteCaptures + whiteCaptures;

            _history.Add(new GameOverHistoryEvent(CurrentBoard.Clone(), newBlackCaptures, newWhiteCaptures));
        }

        public void Undo() {
            _history.RemoveAt(_history.Count - 1);
        }

        public IEnumerable<HistoryEvent> EventFromLastToFirst() {
            for (int i = _history.Count - 1; i >= 0; i--) {
                yield return _history[i];
            }
        }

        public IEnumerable<HistoryEvent> EventFromFirstToLast() {
            for (int i = 0; i < _history.Count; i++) {
                yield return _history[i];
            }
        }

        public HistoryMark GetMark() {
            return new PrivateHistoryMark {
                Event = CurrentHistoryEvent
            };
        }

        public void UndoToMark(HistoryMark mark) {
            PrivateHistoryMark historyMark = (PrivateHistoryMark)mark;
            while (true) {
                if (CurrentHistoryEvent == historyMark.Event) {
                    break;
                }
                Undo();
            }
        }

        private class PrivateHistoryMark : HistoryMark {
            public HistoryEvent Event { get; set; }
        }
    }
}